package com.eonmind.ai.core.draw.platform.mdJounery;

import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.eonmind.ai.config.AiConfig;
import com.eonmind.ai.core.draw.api.IAiDrawService;
import com.eonmind.ai.core.draw.model.AiDrawTaskModel;
import com.eonmind.ai.core.draw.model.AiDrawTaskResModel;
import com.eonmind.ai.core.draw.platform.mdJounery.model.MdCommandRequest;
import com.eonmind.ai.core.draw.platform.mdJounery.model.MdTaskQueryResponse;
import com.eonmind.ai.service.impl.AiDrawPlatformService;
import com.eonmind.ai.utils.HttpUtil;
import com.eonmind.common.contract.constants.HttpHeaders;
import com.eonmind.common.contract.constants.ai.AiDrawPlatformApiConstant;
import com.eonmind.common.contract.enums.ai.AiArtworkStatusEnum;
import com.eonmind.common.contract.enums.ai.AiDrwaPlatformTypeEnum;
import com.eonmind.common.contract.enums.oss.OssUploadEnum;
import com.eonmind.common.contract.enums.user.NoticeTypesEnum;
import com.eonmind.common.contract.exception.BusinessException;
import com.eonmind.common.contract.oss.OssUtil;
import com.eonmind.common.provider.entity.ai.AiArtwork;
import com.eonmind.common.provider.entity.ai.AiDrawPlatform;
import com.eonmind.user.contract.api.UserNoticeFeignService;
import com.eonmind.user.contract.dto.UserNoticeDto;
import com.google.common.collect.Lists;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.net.URL;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.eonmind.common.contract.constants.ai.AiDrawPlatformApiConstant.MidJourney.QUERY_TASK_URL;
import static com.eonmind.common.contract.constants.ai.AiDrawPlatformApiConstant.MidJourney.SubmitTaskResponse.Code;
import static com.eonmind.common.contract.constants.ai.AiDrawPlatformApiConstant.MidJourney.SubmitTaskResponse.Result;

@Slf4j
@Service
public class MdDrawService implements IAiDrawService<MdTaskQueryResponse> {

    @Resource
    private HttpUtil httpUtil;

    @Resource
    private AiDrawPlatformService aiDrawPlatformService;

    @Autowired
    private UserNoticeFeignService userNoticeFeignService;
    @Resource
    private AiConfig aiConfig;
    @Resource
    private OssUtil ossUtil;

    /**
     * 提交一个绘画任务
     *
     * @param aiDrawTask
     * @return 绘画任务的id
     */
    @Override
    public AiDrawTaskResModel submitDrawTask(AiDrawTaskModel aiDrawTask) {
        // 随机获取一个平台
        AiDrawPlatform aiDrawPlatform = aiDrawPlatformService.randomGetOnePlatform(AiDrwaPlatformTypeEnum.MJ.getCode());
        // 获取提交任务的API
        String submitTaskUrl = AiDrawPlatformApiConstant.MidJourney.SUBMIT_TASK_URL;
        // 构建请求
        Map<String, String> headers = buildHeader(aiDrawPlatform);

        // 构建请求体
        String body = buildTaskSubmitRequest(aiDrawTask);

        // 发送请求
        Mono<ResponseEntity<String>> responseEntityMono = httpUtil.post(String.format(submitTaskUrl
                , aiDrawPlatform.getBaseUrl()), headers, body);
        ResponseEntity<String> block = responseEntityMono.block();

        // 验证响应体
        if (block != null && JSONUtil.isTypeJSON(block.getBody())) {
            JSONObject bodyJson = new JSONObject(block.getBody());
            Integer code = bodyJson.getInt(Code);
            if (code == null || code != 1) {
                log.error("Mj请求失败：{}", block);
                throw new BusinessException("Mj请求失败");
            }
            String taskId = bodyJson.getStr(Result);
            AiDrawTaskResModel taskResModel = new AiDrawTaskResModel();
            taskResModel.setTaskId(taskId);
            taskResModel.setPlatformId(aiDrawPlatform.getId());
            return taskResModel;
        } else {
            log.error("请求失败：{}", block);
            throw new BusinessException("请求失败");
        }
    }

    /**
     * 执行Mj的命令
     *
     * @param mdCommandRequest
     * @return
     */
    public AiDrawTaskResModel executeCommandTask(MdCommandRequest mdCommandRequest) {
        // 随机获取一个平台
        AiDrawPlatform aiDrawPlatform = aiDrawPlatformService.randomGetOnePlatform(AiDrwaPlatformTypeEnum.MJ.getCode());
        Map<String, String> reqHead = buildHeader(aiDrawPlatform);
        String requestBody = buildCommandRequestBody(mdCommandRequest);
        String reqUrl = MessageFormat.format(AiDrawPlatformApiConstant.MidJourney.SUBMIT_COMMAND_URL, aiDrawPlatform.getBaseUrl());
        Mono<ResponseEntity<String>> responseEntityMono = httpUtil.post(reqUrl, reqHead, requestBody);
        ResponseEntity<String> block = responseEntityMono.block();
        if (block != null && JSONUtil.isTypeJSON(block.getBody())) {
            JSONObject bodyJson = new JSONObject(block.getBody());
            Integer code = bodyJson.getInt(Code);
            if (code == null || code != 1) {
                log.error("Mj Command 请求失败：{}", block);
                throw new BusinessException("Mj任务提交失败，请稍后再试");
            }
            AiDrawTaskResModel aiDrawTaskResModel = new AiDrawTaskResModel();
            aiDrawTaskResModel.setTaskId(bodyJson.getStr(Result));
            aiDrawTaskResModel.setPlatformId(aiDrawPlatform.getId());
            return aiDrawTaskResModel;
        } else {
            log.error("请求失败：{}", block);
            throw new BusinessException("请求失败");
        }

    }

    private String buildCommandRequestBody(MdCommandRequest mdCommandRequest) {
        String notifyHook = AiDrawPlatformApiConstant.MidJourney.SubmitCommandParams.notifyHook;
        String customId = AiDrawPlatformApiConstant.MidJourney.SubmitCommandParams.CustomId;
        String taskId = AiDrawPlatformApiConstant.MidJourney.SubmitCommandParams.TaskId;
        Map<String, Object> params = new HashMap<>();
        params.put(notifyHook, "http://127.0.0.1:9998/");
        params.put(customId, mdCommandRequest.getCustomId());
        params.put(taskId, mdCommandRequest.getTaskId());
        return JSONUtil.toJsonStr(params);
    }

    private String buildTaskSubmitRequest(AiDrawTaskModel aiDrawTask) {
        String prompt = AiDrawPlatformApiConstant.MidJourney.SubmitTaskParams.Prompt;
        String bser64Array = AiDrawPlatformApiConstant.MidJourney.SubmitTaskParams.Bser64Array;
        Map<String, Object> params = new HashMap<>();

        params.put(prompt, aiDrawTask.getPrompt());
        params.put(bser64Array, aiDrawTask.getBase64Array());
        params.put("notifyHook", "http://127.0.0.1:9998/ai-api/ai/draw/mj/query");

        return JSONUtil.toJsonStr(params);
    }


    /**
     * 查询一个绘画任务
     *
     * @param taskId 绘画任务的id
     * @return 绘画任务的结果
     */
    @Override
    public MdTaskQueryResponse queryDrawTask(AiDrawPlatform aiDrawPlatform, String taskId) {
        String baseUrl = aiDrawPlatform.getBaseUrl();
        String queryTaskUrl = MessageFormat.format(QUERY_TASK_URL, baseUrl, taskId);
        Map<String, String> header = buildHeader(aiDrawPlatform);

        Mono<ResponseEntity<String>> responseEntityMono = httpUtil.get(queryTaskUrl, header, null);
        ResponseEntity<String> block = responseEntityMono.block();
        if (block != null && JSONUtil.isTypeJSON(block.getBody())) {
            JSONObject bodyJson = new JSONObject(block.getBody());
            return JSONUtil.toBean(bodyJson, MdTaskQueryResponse.class);
        } else {
            log.error("绘画任务查询请求失败：{}", block);
            throw new BusinessException("请求失败");
        }
    }

    /**
     * 批量绘画任务
     *
     * @param aiDrawPlatform
     * @param taskIds        绘画任务的id集合
     * @return 绘画任务的结果
     */
    @Override
    public List<MdTaskQueryResponse> queryBatchDrawTask(AiDrawPlatform aiDrawPlatform, List<String> taskIds) {
        if (ObjectUtils.isEmpty(taskIds) || ObjectUtils.anyNull(aiDrawPlatform)) {
            return Lists.newArrayList();
        }
        String baseUrl = aiDrawPlatform.getBaseUrl();
        String queryBatchTaskUrl = MessageFormat.format(AiDrawPlatformApiConstant.MidJourney.QUERY_BATCH_TASK_URL, baseUrl);
        Map<String, String> header = buildHeader(aiDrawPlatform);
        String body = buildBatchQueryBody(taskIds);
        Mono<ResponseEntity<String>> responseEntityMono = httpUtil.post(queryBatchTaskUrl, header, body);
        ResponseEntity<String> block = responseEntityMono.block();
        if (block != null && JSONUtil.isTypeJSONArray(block.getBody())) {
            JSONArray data = JSONUtil.parseArray(block.getBody());
            return JSONUtil.toList(data, MdTaskQueryResponse.class);
        } else {
            log.error("批量绘画任务查询请求失败：{}", block);
            throw new BusinessException("请求失败");
        }
    }

    public void syncMjTaskProgress(AiArtwork aiArtwork, AiDrawPlatform aiDrawPlatform) {
        if (ObjectUtils.isEmpty(aiArtwork) || ObjectUtils.isEmpty(aiArtwork.getTaskId()) || ObjectUtils.isEmpty(aiDrawPlatform)) {
            throw new BusinessException("参数异常,同步失败");
        }
        MdTaskQueryResponse mdTaskQueryResponse = queryDrawTask(aiDrawPlatform, aiArtwork.getTaskId());
        try {
            syncMjArtworkProgress(aiArtwork, mdTaskQueryResponse);
        } catch (Exception e) {
            log.error("执行异常，同步任务失败:{}", e.getMessage(), e);
            throw new BusinessException("执行异常，同步任务失败");
        }
    }

    public AiArtwork syncMjArtworkProgress(AiArtwork t, MdTaskQueryResponse mdTaskQueryResponse) {

        String progressStr = mdTaskQueryResponse.getProgress();
        String pr = progressStr.replaceAll("%", "");
        int progress = Integer.parseInt(pr);
        t.setProgress(progressStr);
        String imageUrl = mdTaskQueryResponse.getImageUrl();
        switch (mdTaskQueryResponse.getStatus()) {
            case "NOT_START" -> t.setStatus(AiArtworkStatusEnum.PENDING_GENERATION.getCode());
            case "SUCCESS" -> {
                t.setStatus(AiArtworkStatusEnum.GENERATED_SUCCESSFULLY.getCode());
                if (StringUtils.isNotBlank(mdTaskQueryResponse.getImageUrl())) {
                    handleImage(t, mdTaskQueryResponse);
                } else {
                    t.setStatus(AiArtworkStatusEnum.GENERATED_FAILED.getCode());
                }
            }
            case "FAILURE" -> {
                if (StringUtils.isNotBlank(imageUrl) && progress >= 90) {
                    handleImage(t, mdTaskQueryResponse);
                    t.setStatus(AiArtworkStatusEnum.GENERATED_SUCCESSFULLY.getCode());
                } else {
                    t.setStatus(AiArtworkStatusEnum.GENERATED_FAILED.getCode());
                    t.setFailReason(mdTaskQueryResponse.getFailReason());
                }

            }
            case "IN_PROGRESS" -> {
                t.setStatus(AiArtworkStatusEnum.GENERATING.getCode());
                if (StringUtils.isBlank(progressStr)) {
                    t.setProgress("0%");
                } else {
                    if (progress >= 90) {
                        t.setProgress("100%");
                        t.setStatus(AiArtworkStatusEnum.GENERATED_SUCCESSFULLY.getCode());
                    }
                }
                Long createTime = t.getCreateTime();
                Date date = new Date(createTime);
                if (date.before(DateTime.now().minusMinutes(aiConfig.getMaxDrawTaskExpire()).toDate())) {
                    t.setStatus(AiArtworkStatusEnum.GENERATED_FAILED.getCode());
                    t.setFailReason("任务超时");
                }
                if (StringUtils.isNotBlank(mdTaskQueryResponse.getImageUrl())) {
                    handleImage(t, mdTaskQueryResponse);
                }
            }
        }
        if (t.getStatus().equals(AiArtworkStatusEnum.GENERATED_FAILED.getCode())) {
            sendDrawTaskNotice(t.getUserId(), "AI绘图任务失败", "任务失败原因：" + t.getFailReason());
        }
        if (t.getStatus().equals(AiArtworkStatusEnum.GENERATED_SUCCESSFULLY.getCode())) {
            sendDrawTaskNotice(t.getUserId(), "AI绘图任务成功", "AI绘画成功生成，请前往页面查看");
        }
        return t;
    }

    private void sendDrawTaskNotice(Long userId, String title, String content) {
        UserNoticeDto.UserSendNoticeRequest userSendNoticeRequest = new UserNoticeDto.UserSendNoticeRequest();
        userSendNoticeRequest.setUserId(userId);
        userSendNoticeRequest.setNoticeType(NoticeTypesEnum.SYSTEM_NOTICE);
        userSendNoticeRequest.setNoticeTitle(title);
        userSendNoticeRequest.setNoticeContent(content);
        com.eonmind.common.contract.data.response.ResponseEntity<Void> voidResponseEntity = userNoticeFeignService.sendUserNotice(userSendNoticeRequest);
        log.info("用户发送通知：{}", JSONUtil.toJsonStr(voidResponseEntity));
    }

    private void handleImage(AiArtwork t, MdTaskQueryResponse mdTaskQueryResponse) {
        String imageUrl = mdTaskQueryResponse.getImageUrl();
        log.info("[draw 同步任务] 任务图片地址：{}", imageUrl);
        URL url = UrlBuilder.of(imageUrl).toURL();
        int count = 5;
        while (count-- > 0) {
            try {
                String fileName = RandomStringUtils.randomAlphabetic(8) + ".png";
                ossUtil.uploadFile(OssUploadEnum.AI_DRAW_UPLOAD, fileName, url.openStream());
                String fileUrl = ossUtil.getFileUrl(OssUploadEnum.AI_DRAW_UPLOAD, fileName);
                log.info("[draw 同步任务] 图片上传成功：{}", fileUrl);
                t.setImgList(List.of(fileUrl));
                // 由于MJ 可以对生图进行 放大等操作 设置按钮数组到任务中保存
                t.setExtraJson(JSONUtil.toJsonStr(mdTaskQueryResponse.getButtons()));
                return;
            } catch (Exception e) {
                log.error("图片上传失败：{}", imageUrl, e);

            }
        }
        t.setStatus(AiArtworkStatusEnum.GENERATED_FAILED.getCode());
        t.setFailReason("图片上传失败");
    }

    private String buildBatchQueryBody(List<String> taskIds) {
        JSONObject params = new JSONObject();
        params.append("ids", taskIds);
        return JSONUtil.toJsonStr(params);
    }


    private Map<String, String> buildHeader(AiDrawPlatform aiDrawPlatform) {
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.AUTHORIZATION, "Bearer " + aiDrawPlatform.getApiKey());
        return headers;
    }
}
